from tkinter.messagebox import NO
import time
import caminput as ci
import tcpinput as ti
import cv2 as cv
import wx
import numpy as np
import core_algorithm as core
from multiprocessing import Process, Queue
import threading
import sync_thread
input_queue = Queue(1)
output_queue = Queue(1)
last_frame = None
yolov_perf = []
controller = None # Car
sync = None
last_status = ""
class FramePanel(wx.Frame):
    input_menu_id = 100
    input_mode = 0
    camera_mode = 1001
    tcp_mode = 1002
    camera_menu_item_id = 101
    tcp_menu_item_id = 102
    input_thread = None
    bitmap = None
    font = None

    def __init__(self, *args, **kw):
        self.filters = []
        self.filter_id = 0
        self.carAddress = '0.0.0.0'
        self.set = 0
        self.is_disconnect = False

        fps = kw.get('fps', 24)
        del kw['fps']
        if 'size' not in kw:
            kw['size'] = (650, 550)
        super(FramePanel, self).__init__(*args, **kw)

        self.font = wx.Font(30, wx.ROMAN, wx.ITALIC, wx.NORMAL)
        self._make_menu_bar()
        self.timer = wx.Timer(self, 1)
        self.timer.Start(int(1000. / fps))

        self.top_pnl = wx.Panel(self, -1)

        wx.StaticText(self.top_pnl, pos=(5, 5), label='过滤器：')
        self.filters_choice = wx.Choice(parent=self.top_pnl, pos=(62, 2), size=(60, 40), choices=["无"])
        self.filters_choice.Bind(wx.EVT_CHOICE, self._on_filters_choice)

        self.pnl = wx.Panel(self.top_pnl, -1, pos=(0, 30), size=(650, 500))
        self.pnl.SetDoubleBuffered(True)
        self.pnl.SetBackgroundColour(wx.WHITE)
        self.pnl.Bind(wx.EVT_PAINT, self._on_paint)

        self.Bind(wx.EVT_TIMER, self._on_timer, id=1)
        self.Bind(wx.EVT_CLOSE, self._on_close)

    def _make_menu_bar(self):
        open_menu = wx.Menu()
        open_menu.Append(self.camera_menu_item_id, "摄像头", '以摄像头为输入')
        open_menu.Append(self.tcp_menu_item_id, "TCP连接", '以网络源为输入')
        open_menu.Bind(wx.EVT_MENU, self._menu_open_camera, id=self.camera_menu_item_id)
        open_menu.Bind(wx.EVT_MENU, self._menu_open_tcp, id=self.tcp_menu_item_id)
        menu_bar = wx.MenuBar()
        menu_bar.Append(open_menu, "打开")
        self.SetMenuBar(menu_bar)

    def add_filter(self, label, fi):
        self.filters_choice.Append(label)
        self.filters.append(fi)
        self.filter_id += 1
        self.filters_choice.SetSelection(self.filter_id)

    def do_filters(self, data):
        if self.filter_id <= 0 or self.filter_id > len(self.filters):
            return data
        return self.filters[self.filter_id - 1](data)

    def _init_input_thread(self):
        self.input_thread.start()
        self.input_thread.retrieve()

    def wait_input_thread_quit(self):
        self.input_thread.set_quit()
        self.input_thread.join()

    def _menu_open_camera(self, event):
        if self.input_mode == self.camera_mode:
            return
        if self.input_mode != 0:
            self.wait_input_thread_quit()
            self.bitmap = None
        self.input_mode = self.camera_mode
        self.input_thread = ci.CameraInput()
        self._init_input_thread()

    def handle_disconnect(self):
        '''处理连接中断'''
        pass

    def handler_connect(self, addr):
        global controller
        global sync
        global last_status  
        controller = core.Car(addr)
        sync = sync_thread.SyncThread(addr)
        sync.start()
        controller.inter_status = sync.GetStatus()
        print(f'forward_count = {controller.inter_status.forward_count}')
        last_status = controller.inter_status.to_str();
        print(f'call handler_connect:\t{addr}')

    def _menu_open_tcp(self, event):
        if self.input_mode == self.tcp_mode:
            return
        if self.input_mode != 0:
            self.wait_input_thread_quit()
            self.bitmap = None
            self.pnl.Refresh(eraseBackground=True)
        self.input_mode = self.tcp_mode
        self.input_thread = ti.TcpInput()
        self.input_thread._on_connect_cb = self.handler_connect
        self.input_thread.on_disconnect = self.handle_disconnect
        self._init_input_thread()

    def _on_filters_choice(self, event):
        self.filter_id = self.filters_choice.GetSelection()

    def _on_timer(self, event):
        if self.input_thread is None:
            self.Refresh(eraseBackground=False)
            return
        if not self.input_thread.is_alive:
            self.timer.Stop()
            return

        data = self.input_thread.retrieve()

        if data is None:
            return
        distan = None
        curr_time_stamp = None
        if type(data) == type(()) and len(data) > 1:
            # curr_time_stamp = time.time()
            # print(f'curr_stamp:{curr_time_stamp:.3f} car_time_stamp: {data[2]} |  diff: {curr_time_stamp - data[2]}')
            curr_time_stamp = data[2]
            distan = data[1]
            data = data[0]
        h, w = data.shape[:2]
        data = self.do_filters((data, distan, curr_time_stamp))
        # test data is not None
        if data is None:
            return
        data = cv.cvtColor(data, cv.COLOR_BGR2RGB)
        if self.bitmap is not None:
            self.bitmap.CopyFromBuffer(data)
        else:
            self.bitmap = wx.Bitmap.FromBuffer(w, h, data)
        self.Refresh(eraseBackground=False)

    def _on_close(self, event):
        global yolov_perf
        if self.input_thread is not None:
            self.input_thread.set_quit()
        
        self.Destroy()

    def _on_paint(self, event):
        device_context = wx.BufferedPaintDC(self.pnl)
        if self.bitmap is None:
            device_context.SetBackground(wx.BLACK_BRUSH)
            device_context.Clear()
            device_context.SetTextForeground(wx.WHITE)
            device_context.SetFont(self.font)
            device_context.DrawText("No signal.", 250, 160)
        else:
            device_context.DrawBitmap(self.bitmap, 0, 0)


# 在这里写函数处理数据
# 函数的输入必须一个[image]类型的参数，也必须返回[image]
# 以下三个为演示
# 注意要注册你编写的函数

def gray_filter(data):
    return data[0]



class YoloProcess(Process):
    def __init__(self, generator, in_queue: Queue, ou_queue: Queue):
        super().__init__(daemon=True)
        self.gen = generator
        self.in_queue: Queue = in_queue
        self.out_queue: Queue = ou_queue
        

    def run(self):
        global yolov_perf
        next(self.gen)
        while True:
            input_image, distance, time_stamp = input_queue.get()
            t_start = time.perf_counter()
            self.out_queue.put((self.gen.send(input_image), distance, time_stamp))
            print(f'yolov5 network time cost: {time.perf_counter() - t_start:.3f}s')
            

class DemoApp(wx.App):

    def OnInit(self):
        # ======= 启动各个进程和线程 START ===========
        try:
            from yolov5 import detect
        except:
            print('缺少yolov5')
            exit(1)
        worker = YoloProcess(detect.run(source=':mem:'), input_queue, output_queue)
        worker.start()
        
        # ======= 启动各个进程和线程 END ===========

        panel = FramePanel(None, title='FramePanel', fps=24)

        panel.add_filter('预览', gray_filter)

        
        def yolov_func(data):
            '''  '''
            global last_frame
            global last_status
            if not input_queue.full():
                input_queue.put_nowait(data)
            if not output_queue.empty():
                (last_frame, detected_objs), distance, time_stamp = output_queue.get()

                print(f'distance = {distance} det = {detected_objs}')
                # filter_obj = [o for o in detected_objs if o[0] in ['bottle','wine glass','vase']]
                # print(f'time_stamp_diff: {int(time.time()) - time_stamp}') 
                # controller.heading(detected_objs, distance)
                assert controller is not None
                if controller.inter_status.to_str() != last_status:
                    print('change detected')
                    last_status = controller.inter_status.to_str()
                    sync.SendStatus(controller.inter_status)
            return last_frame
            

        panel.add_filter('yolov5', yolov_func)

        self.SetTopWindow(panel)
        panel.Show()

        return True



app = DemoApp()
app.MainLoop()
